package org.dreamwork.dsi.embedded.httpd.starter;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.dreamwork.concurrent.Looper;
import org.dreamwork.dsi.embedded.httpd.support.BackendServlet;
import org.dreamwork.injection.AConfigured;
import org.dreamwork.injection.IObjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;

@Resource
public class EmbeddedTomcatStarter {
    private final Logger logger = LoggerFactory.getLogger (EmbeddedTomcatStarter.class);

    private final Tomcat tomcat = new Tomcat ();

    @AConfigured ("${embedded.httpd.context-path}")
    private String contextPath = "/";

    @AConfigured ("${embedded.httpd.base}")
    private String docBase = "../webapp";

    @AConfigured ("${embedded.httpd.port}")
    private int port = 9090;

    @AConfigured ("${embedded.httpd.host}")
    private String host = "127.0.0.1";

    @AConfigured ("${embedded.httpd.api-mapping}")
    private String mapping = "/api";

    @Resource
    private IObjectContext context;

    @SuppressWarnings ("unused")
    public String getContextPath () {
        return contextPath;
    }

    @SuppressWarnings ("unused")
    public void setContextPath (String contextPath) {
        this.contextPath = contextPath;
    }

    @SuppressWarnings ("unused")
    public String getDocBase () {
        return docBase;
    }

    @SuppressWarnings ("unused")
    public void setDocBase (String docBase) {
        this.docBase = docBase;
    }

    @SuppressWarnings ("unused")
    public int getPort () {
        return port;
    }

    @SuppressWarnings ("unused")
    public void setPort (int port) {
        this.port = port;
    }

    @SuppressWarnings ("unused")
    public String getHost () {
        return host;
    }

    @SuppressWarnings ("unused")
    public void setHost (String host) {
        this.host = host;
    }

    @SuppressWarnings ("unused")
    public String getMapping () {
        return mapping;
    }

    @SuppressWarnings ("unused")
    public void setMapping (String mapping) {
        this.mapping = mapping;
    }

    @PostConstruct
    public void startTomcat () throws Exception {
        if (logger.isTraceEnabled ()) {
            logger.trace ("starting embedded tomcat ...");
            logger.trace ("    context-path = {}", contextPath);
            logger.trace ("        doc-base = {}", docBase);
            logger.trace ("            port = {}", port);
            logger.trace ("            host = {}", host);
        }

        File base = new File ("../embedded-httpd");
        if (!base.exists () && !base.mkdirs ()) {
            throw new IOException ("cannot create base dir: " + base.getCanonicalPath ());
        }

        File serverRoot = new File (base, "webapps");
        if (!serverRoot.exists () && !serverRoot.mkdirs ()) {
            throw new IOException ("cannot create webapps dir: " + serverRoot.getCanonicalPath ());
        }

        File root = new File (serverRoot, "ROOT");
        if (!root.exists () && !root.mkdirs ()) {
            throw new IOException ("cannot create root dir: " + root.getCanonicalPath ());
        }

        tomcat.setBaseDir (base.getCanonicalPath ());
        tomcat.setPort (port);
        tomcat.setHostname (host);
        tomcat.getConnector ();
        if (contextPath.charAt (0) != '/') {
            contextPath = '/' + contextPath;
        }

        File webapp = new File (docBase);
        if (!webapp.exists ()) {
            webapp = new File (System.getProperty ("java.io.tmpdir"), ".embedded-httpd");
            if (!webapp.exists () && !webapp.mkdirs ()) {
                throw new IOException ("cannot find webapp: " + webapp.getCanonicalPath ());
            }
        }

        StandardContext webContext = (StandardContext) tomcat.addWebapp (contextPath, webapp.getCanonicalPath ());
        context.register ("webContext", webContext.getServletContext ());
        ServletContext app = webContext.getServletContext ();
        // 在 ServletContext 中设置 IObjectContext 实例，键名为 org.dreamwork.injection.IObjectContext
        app.setAttribute (IObjectContext.class.getCanonicalName (), context);

        Wrapper w = Tomcat.addServlet (webContext, "backend", BackendServlet.class.getCanonicalName ());
        w.addMapping ("/" + mapping + "/*");

        Looper.invokeLater (() -> {
            try {
                tomcat.start ();
                logger.info ("embedded tomcat started.");
            } catch (LifecycleException e) {
                e.printStackTrace ();
            }
        });
    }

    @PreDestroy
    public void destroy () throws LifecycleException {
        tomcat.stop ();
        logger.info ("tomcat stopped.");
    }
}